﻿//题目描述
//给定一个无向图，包含n个点，m条边（没有重边和自环）。
//求图中所有联通块的大小。
//输入格式
//第一行：两个整数n，m，表示点数和边数。（1≤n, m≤2×10^5）
//接下来m行：每行两个整数ui​, vi​，表示在𝑢𝑖​和𝑣𝑖​之间存在一条无向边。
//输出格式
//从小到大输出所有联通块的大小。
//样例输入
//5 2
//1 2
//1 3
//样例输出
//1 1 3
//提示
//所有联通块为：{1,2,3 }，{4}，{ 5 }。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
int pre[N],cnt[N];
int root(int x) {
	return pre[x] = (pre[x] == x ? x:root(pre[x]));
	//root(x) 是一个查找操作，用于找到节点 x 的根节点。我们使用了路径压缩的优化，
	// 使得每次查找时，会将路径上的所有节点直接连接到根节点，从而加速后续查询。
}
void merge(int x, int y) {
	pre[root(x)] = root(y);
	//merge(x, y) 用于合并 x 和 y 所在的联通块。通过查找两个节点的根节点，并将其中一个根节点指向另一个根节点，完成合并。
}
void solve() {
	int n, m; cin >> n >> m;
	for (int i = 1; i <= n; ++i)pre[i] = i;
	//初始化并查集的父节点数组，保证每个节点的父节点都是它自己，表示每个节点初始是一个独立的联通块。
	for (int i = 1; i <= m; ++i) {
		int u, v; cin >> u >> v;
		merge(u, v);
	}
	for (int i = 1; i <= n; ++i)cnt[root(i)]++;
	//这一行遍历每个节点，查找其根节点，并统计每个联通块的大小。cnt[root(i)]++ 会把每个节点的数量加到其根节点对应的联通块大小中
	vector<int>v;
	for (int i = 1; i <= n; ++i)			
	//遍历每个节点的 cnt 数组，如果 cnt[i] > 0，说明节点 i 是某个联通块的根节点，cnt[i] 存储了这个联通块的大小，就把它加入 v 中。
		if (cnt[i])
			v.push_back(cnt[i]);
	sort(v.begin(), v.end());
	for (auto& i : v)cout << i << ' ';
}
int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	solve();
	return 0;
} 